""" token校验中间件
@Project: Rebar
@File: certificate_middleware.py
@Author: hzc
@Date: 2023/3/21 16:40
"""
import re
from starlette.requests import Request
from starlette.responses import JSONResponse

from config import PASS_URI, ROUTER_PREFIX, USER_TOKEN, SUPER_TOKEN
# from model.db.mapper import get_role, get_role_by_name
# from model.db.mapper.user_mapper import get_user
# from model.db.mapper.permission_mapper import check_permission
from model.params import RoleNameEnum
from model.redis.utils import redis_verify_and_refresh_token
from util import error_res, ErrorCode, DataNotExist
from model.db.mapper.users_mapper import get_user_by_id
from util.access_token import parse_token
from util.exception_handlers import UserNoPermission

"""request.scope详细信息
{'scope': {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 
'server': ('127.0.0.1', 8000), 'client': ('127.0.0.1', 59865), 'scheme': 'http', 'method': 'GET', 'root_path': '', 
'path': '/openapi.json', 'raw_path': b'/openapi.json', 'query_string': b'', 'headers': [(b'host', b'localhost:8000'), 
(b'connection', b'keep-alive'), (b'sec-ch-ua', b'"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"'), 
(b'accept', b'application/json,*/*'), (b'sec-ch-ua-mobile', b'?0'), (b'users-agent', b'Mozilla/5.0 (Windows NT 10.0; 
Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.44'), 
(b'sec-ch-ua-platform', b'"Windows"'), (b'sec-fetch-site', b'same-origin'), (b'sec-fetch-mode', b'cors'), 
(b'sec-fetch-dest', b'empty'), (b'referer', b'http://localhost:8000/docs'), (b'accept-encoding', b'gzip, deflate, 
br'), (b'accept-language', b'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6')], 'app': <fastapi.applications.FastAPI 
object at 0x0000024817C62C40>}, '_receive': <bound method RequestResponseCycle.receive of 
<uvicorn.protocols.http.h11_impl.RequestResponseCycle object at 0x0000024817CEB040>>, '_send': <function empty_send 
at 0x00000248179C9AF0>, '_stream_consumed': False, '_is_disconnected': False, '_form': None} 
"""


class TokenVerifyMiddleware:  # token校验中间件

    def __init__(self, app=None):
        self.app = app

    @staticmethod
    def permission_check(url_scope, group_id):
        """
        权限校验
        :param group_id:
        :param url_scope: 访问路径
        :return:
        """
        # 角色管理权限校验
        # if re.match("^/role.*?", url_scope):
        #     role = get_role_by_name(role_name)
        #     if role.role_rank.value <= 1:
        #         raise UserNoPermission()

    async def __call__(self, request: Request, call_next):
        """
        当实例化类时执行
        :param request: 客户端发送的请求信息
        :param call_next: 校验通过后应该执行的方法
        :return:
        """

        # 预请求直接放行
        if request.scope['method'] == 'OPTIONS':
            return await call_next(request)
        """ 判断访问路径是否需要拦截，如登录、注册、查看文档等接口无需携带token
        这里去掉了公共路由前缀，因为PASS_URL中记录的scope也没有公共前缀
        """
        url_scope = request.scope["path"].replace(ROUTER_PREFIX, "")
        if url_scope in PASS_URI or re.match(r'^/public/.*?', url_scope):
            return await call_next(request)
        # 验证token
        token = request.headers.get("token")
        if token is None:  # 判断请求头中是否有token
            return error_res(ErrorCode.MISSING_TOKEN)
        payload = parse_token(token)
        if payload is None:  # 判断token是否正确
            return error_res(ErrorCode.INVALID_TOKEN)
        try:  # 判断token是否过期
            if payload.get("role_name") == RoleNameEnum.Super.value:
                return await redis_verify_and_refresh_token(request, payload, token, call_next, SUPER_TOKEN)

            user = get_user_by_id(payload.get("user_id"))

            try:
                # 检查权限
                # check_permission(url_scope, user.group_id)
                # 更新token
                return await redis_verify_and_refresh_token(request, payload, token, call_next, USER_TOKEN)
            except UserNoPermission as e:
                return error_res(ErrorCode.USER_NO_PERMISSION)
        except DataNotExist as e:
            res_code = ErrorCode.DATA_NOT_EXISTED
            res_code['message'] = res_code["message"].format(e.detail)
            return error_res(res_code)
